package com.ecoolex.hotel.saas.core.manager;

import com.ecoolex.framework.common.enums.ResultCode;
import com.ecoolex.framework.common.util.BizAssert;
import com.ecoolex.framework.common.util.Check;
import com.ecoolex.hotel.saas.core.dao.PaymentOrderDAO;
import com.ecoolex.hotel.saas.core.service.WeChatPayService;
import com.ecoolex.hotel.saas.entity.PaymentOrder;
import com.ecoolex.hotel.saas.entity.PaymentOrder.Column;
import com.ecoolex.hotel.saas.entity.PaymentOrderExample;
import com.ecoolex.hotel.saas.entity.PaymentOrderRecord;
import com.ecoolex.hotel.saas.entity.PresaleCouponOrder;
import com.ecoolex.hotel.saas.enums.PaymentOrderRecordOperType;
import com.ecoolex.hotel.saas.enums.PaymentOrderRecordResultStatus;
import com.ecoolex.hotel.saas.enums.PaymentOrderStatus;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.Date;
import java.util.List;
import java.util.function.BiFunction;

import com.ecoolex.hotel.saas.enums.PresaleCouponOrderPaymentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class PaymentOrderManager {
	
	Logger log = LoggerFactory.getLogger(PaymentOrderManager.class);
	
    @Autowired
    private PaymentOrderDAO dao;
    
    @Autowired
    private PaymentOrderRecordManager paymentOrderRecordManager;
    
    @Autowired
    private PresaleCouponOrderManager presaleCouponOrderManager;
    
    @Autowired
    private WeChatPayService weChatPayService;
    
    //预售券订单
    private static final String TYPE_PRESALE_COUPON_ORDER = "PCO";
    
    @Transactional
	public <T> T couponOrderPayApply(PresaleCouponOrder order, Integer paymentType, String createUser, BiFunction<PaymentOrder, PaymentOrderRecord, T> biFunction) {
    	PaymentOrder record = null;
		// 检查支付订单是否存在
	    record = getByBizTypeAndOrderNo(TYPE_PRESALE_COUPON_ORDER, order.getOrderNo());
	    if (Check.isNull(record)) {
	        // 创建订单
	        record = new PaymentOrder();
	        record.setUserId(order.getUserId());
    		record.setBizType(TYPE_PRESALE_COUPON_ORDER);
	        record.setPaymentType(paymentType);
	        record.setOrderNo(order.getOrderNo());
	        record.setPrepaidCurrencyType("CNY");
	        record.setPrepaidPrice(order.getRealPrice());
	        record.setStatus(PaymentOrderStatus.PAY_APPLY.real());
	        record.setDeleted(PaymentOrder.NOT_DELETED);
	        record.setCreateUser(createUser);
	        record.setCreateTime(Date.from(Instant.now()));
	        record.setUpdateUser(createUser);
	        record.setUpdateTime(Date.from(Instant.now()));
	        dao.insert(record);
	    } else {
	        // 失败才能发起
	        BizAssert.isTrue(PaymentOrderStatus.PAY_FAILED.equal(record.getStatus()),
	                ResultCode.BIZ.build(10),
	                "订单支付中或已支付, 无法发起支付");
	        // 变更状态为支付中
	        record.setStatus(PaymentOrderStatus.PAY_APPLY.real());
	        record.setUpdateUser(createUser);
	        record.setUpdateTime(Date.from(Instant.now()));
	
	        PaymentOrderExample example = new PaymentOrderExample();
	        example.createCriteria()
	                .andPaymentOrderIdEqualTo(record.getPaymentOrderId())
	                .andStatusEqualTo(PaymentOrderStatus.PAY_FAILED.real())
	                .andDeletedEqualTo(PaymentOrder.NOT_DELETED);
	
	        int row = dao.updateByExampleSelective(record,
	                example,
	                Column.status,
	                Column.updateUser,
	                Column.updateTime);
	
	        BizAssert.isTrue(row == 1,
	                ResultCode.BIZ.build(10),
	                "订单支付中或已支付, 无法发起支付");
	    }
	
	    // 创建一条发起记录
	    PaymentOrderRecord log = paymentOrderRecordManager.create(record, paymentType, PaymentOrderRecordOperType.ORDER_APPLY);
	    try {
	        // 执行
	        T result = biFunction.apply(record, log);
	        log.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
	        return result;
	    } catch (Exception e) {
	        // 日志记录为异常
	        log.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
	        log.setErrorMessage(e.getMessage());
	        // 更新支付订单为异常
	        record.setStatus(PaymentOrderStatus.PAY_FAILED.real());
	        record.setUpdateUser(createUser);
	        record.setUpdateTime(Date.from(Instant.now()));
	
	        PaymentOrderExample example = new PaymentOrderExample();
	        example.createCriteria()
	                .andPaymentOrderIdEqualTo(record.getPaymentOrderId())
	                .andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
	                .andDeletedEqualTo(PaymentOrder.NOT_DELETED);
	
	        dao.updateByExampleSelective(record,
	                example,
	                Column.status,
	                Column.updateUser,
	                Column.updateTime);
	        return null;
	    } finally {
	        // 保存记录结果
	        paymentOrderRecordManager.add(log, createUser);
	    }
	}
    
    /*
		修改支付状态并通知
	 */
	@Transactional
	public void updatePayStatusAndNotify(PaymentOrder order, PaymentOrderRecord record, String updateUser) {
	    if (PaymentOrderRecordResultStatus.ONGOING.equal(record.getResultStatus())) {
	        // 支付中, 不处理
	        return;
	    }
	    // 更新条件
	    PaymentOrderExample example = new PaymentOrderExample();
	    example.createCriteria()
	            .andPaymentOrderIdEqualTo(order.getPaymentOrderId())
	            .andDeletedEqualTo(PaymentOrder.NOT_DELETED);
	
	    // 更新支付订单状态
	    if (PaymentOrderRecordResultStatus.SUCCEED.equal(record.getResultStatus())) {
	        // 如果通知成功, 则更新成功的支付信息
	        order.setPaymentType(record.getPaymentType());
	        order.setPaymentCurrencyType(record.getCurrencyType());
	        order.setPaymentPrice(record.getTransAmount());
	        order.setPaymentNo(record.getTransNo());
	        order.setPaymentTime(record.getTransTime());
	        order.setStatus(PaymentOrderStatus.PAY_SUCCEED.real());
	        order.setUpdateUser(updateUser);
	        order.setUpdateTime(Date.from(Instant.now()));
	
	        int row = dao.updateByExampleSelective(order,
	                example,
	                Column.paymentType,
	                Column.paymentCurrencyType,
	                Column.paymentPrice,
	                Column.paymentNo,
	                Column.paymentTime,
	                Column.status,
	                Column.updateUser,
	                Column.updateTime);
	
	        // 如果更新失败则返回
	        if (row != 1) {
	            return;
	        }
	    } else {
	        // 失败则更新状态
	        order.setStatus(PaymentOrderStatus.PAY_FAILED.real());
	        order.setUpdateUser(updateUser);
	        order.setUpdateTime(Date.from(Instant.now()));
	
	        int row = dao.updateByExampleSelective(order,
	                example,
	                Column.status,
	                Column.updateUser,
	                Column.updateTime);
	
	        // 如果更新失败则返回
	        if (row != 1) {
	            return;
	        }
	    }
	
	    // 成功则通知相关业务方
	    if (order.getBizType().equals(TYPE_PRESALE_COUPON_ORDER)) {
	    	presaleCouponOrderManager.payNotify(order, updateUser);
	    }
	}
	
	/**
	 * 退款申请
	 * @param orderNo
	 * @param createUser
	 */
	public Boolean refundApply(String orderNo,BigDecimal refundPrice,String refundDesc,Integer storeId, String createUser) {
		PaymentOrder paymentOrder = getByBizTypeAndOrderNo(TYPE_PRESALE_COUPON_ORDER, orderNo);
		BizAssert.isTrue(Check.notNull(paymentOrder), ResultCode.BIZ.build(10), "支付订单未找到, 无法发起退款");
		
		//支付成功或者退款失败状态才能重新发起退款
		BizAssert.isTrue(PaymentOrderStatus.PAY_SUCCEED.equal(paymentOrder.getStatus()) || 
			PaymentOrderStatus.REFUND_FAILED.equal(paymentOrder.getStatus()),
			ResultCode.BIZ.build(11), "订单必须已支付/退款失败, 才能发起退款");
		paymentOrder.setStatus(PaymentOrderStatus.REFUND_APPLY.real());
		paymentOrder.setUpdateUser(createUser);
		paymentOrder.setUpdateTime(Date.from(Instant.now()));

        int row = dao.updateByPrimaryKeySelective(paymentOrder,
                Column.status,
                Column.updateUser,
                Column.updateTime);

        BizAssert.isTrue(row == 1,
                ResultCode.BIZ.build(12),
                "订单发起退款失败");

        // 创建一条发起记录
        PaymentOrderRecord record = paymentOrderRecordManager.create(paymentOrder, paymentOrder.getPaymentType(), PaymentOrderRecordOperType.REFUND_APPLY);
        try {
            if (PresaleCouponOrderPaymentType.WECHAT_PAY.real().equals(paymentOrder.getPaymentType())) {
            	weChatPayService.refundApply(paymentOrder,refundPrice, refundDesc,storeId,record);
            }
            record.setResultStatus(PaymentOrderRecordResultStatus.SUCCEED.real());
        } catch (Exception e) {
        	log.error("发起退款异常：",e);
            // 记录详情状态
        	record.setResultStatus(PaymentOrderRecordResultStatus.FAILED.real());
        	record.setErrorMessage(e.getMessage());
            // 设置退款失败
            paymentOrder.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
            paymentOrder.setUpdateUser(createUser);
            paymentOrder.setUpdateTime(Date.from(Instant.now()));

            dao.updateByPrimaryKeySelective(paymentOrder,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);
        } finally {
            // 记录调用详情
            paymentOrderRecordManager.add(record, createUser);
        }
        return PaymentOrderRecordResultStatus.SUCCEED.equal(record.getResultStatus());
	}
	
	/*
     * 修改退款状态并通知
     */
    @Transactional
    public void updateRefundStatusAndNotify(PaymentOrder order, PaymentOrderRecord record, String updateUser) {
        if (PaymentOrderRecordResultStatus.ONGOING.equal(record.getResultStatus())) {
            // 支付中, 不处理
            return;
        }

        // 更新条件
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria()
                .andPaymentOrderIdEqualTo(order.getPaymentOrderId())
                .andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real())
                .andDeletedEqualTo(PaymentOrder.NOT_DELETED);

        // 更新支付订单状态
        if (PaymentOrderRecordResultStatus.SUCCEED.equal(record.getResultStatus())) {
            // 如果通知成功, 则更新成功的支付信息
        	BigDecimal newRefundPrice;
        	if(Check.notNull(order.getRefundPrice())) {
        		newRefundPrice = order.getRefundPrice().add(record.getTransAmount());
        	}else {
        		newRefundPrice = record.getTransAmount();
        	}
        	
            order.setRefundPrice(newRefundPrice);
        	order.setStatus(PaymentOrderStatus.REFUND_SUCCEED.real());
            order.setRefundTime(record.getTransTime());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.refundPrice,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        } else {
            // 失败则更新状态
            order.setStatus(PaymentOrderStatus.REFUND_FAILED.real());
            order.setUpdateUser(updateUser);
            order.setUpdateTime(Date.from(Instant.now()));

            int row = dao.updateByExampleSelective(order,
                    example,
                    Column.status,
                    Column.updateUser,
                    Column.updateTime);

            // 如果更新失败则返回
            if (row != 1) {
                return;
            }
        }

        // 通知相关业务方
//        if (order.getBizType().equals(TYPE_PRESALE_COUPON_ORDER)) {
//        	presaleCouponOrderManager.refundNotify(order, updateUser);
//        }
    }
    
    public PaymentOrder getByBizTypeAndOrderNo(String bizType, String orderNo) {
        PaymentOrderExample example = new PaymentOrderExample();
        example.createCriteria().andBizTypeEqualTo(bizType).andOrderNoEqualTo(orderNo);
        return dao.selectOneByExample(example);
    }
    
    public List<PaymentOrder> queryPayApplyTimeOut(Date time,int size){
    	PaymentOrderExample example = new PaymentOrderExample();
    	example.createCriteria()
    	.andStatusEqualTo(PaymentOrderStatus.PAY_APPLY.real())
    	.andDeletedEqualTo(PaymentOrder.NOT_DELETED)
    	.andUpdateTimeLessThanOrEqualTo(time);
    	example.orderBy(Column.updateTime.asc());
    	example.limit(size);
    	return dao.selectByExample(example);
    }
    
    public List<PaymentOrder> queryRefundApplyTimeOut(Date time,int size){
    	PaymentOrderExample example = new PaymentOrderExample();
    	example.createCriteria()
    	.andStatusEqualTo(PaymentOrderStatus.REFUND_APPLY.real())
    	.andDeletedEqualTo(PaymentOrder.NOT_DELETED)
    	.andUpdateTimeLessThanOrEqualTo(time);
    	example.orderBy(Column.updateTime.asc());
    	example.limit(size);
    	return dao.selectByExample(example);
    }
}